package com.common.aspect;

import com.alibaba.fastjson.JSON;
import com.common.annotation.ApiLogDetail;
import com.common.base.BaseEnum;
import com.common.kafka.DataPushApi;
import com.common.util.DingDingUtils;
import com.common.vo.YsLogsVo;
import lombok.extern.slf4j.Slf4j;
import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.*;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.context.annotation.Lazy;
import org.springframework.stereotype.Component;
import javax.annotation.Resource;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.*;

/**
 * @version: V1.0
 * @author: szw
 * @className: LogAspect
 * @packageName: LogAspect
 * @description: aop切面核心类
 * @data: 2021-1-27
 **/
@Aspect
@Component
@Slf4j
public class LogAspect {

    @Resource
    @Lazy
    private DataPushApi dataPushApi;

    @Pointcut("@annotation(com.common.annotation.ApiLogDetail)")
    public void operationLog(){}

    /**
     * 环绕增强，相当于MethodInterceptor
     */
    @Around("operationLog()")
    public Object doAround(ProceedingJoinPoint joinPoint) throws Throwable {
        Object res = null;
        long time = System.currentTimeMillis();
        try {
            // 获取返回结果集
            res = joinPoint.proceed(joinPoint.getArgs());
            time = System.currentTimeMillis() - time;
            return res;
        } finally {
            try {
                //方法执行完成后增加日志
                addOperationLog(joinPoint,res,time);
            }catch (Exception e){
                log.error("LogAspect 操作失败：" + e.getMessage());
                DingDingUtils.sendTextToDD("警告：ys 公共模块，LogAspect 操作失败，堆栈信息"+e.getMessage());
            }
        }
    }

    /**
     * 组装日志对象
     * @param joinPoint
     * @param res
     * @param time
     */
    private void addOperationLog(JoinPoint joinPoint, Object res, long time){
        MethodSignature signature = (MethodSignature)joinPoint.getSignature();
        YsLogsVo ysLogsVo = new YsLogsVo();
        ysLogsVo.setIpAddress(getIp().getHostAddress());
        ysLogsVo.setTargetName(signature.getDeclaringTypeName());
        ysLogsVo.setServiceName(signature.getDeclaringTypeName() + "." + signature.getName());
        //目前鉴权服务没加暂时先注释
//        ysLogsVo.setOperationId(Long.valueOf("#{currentUserId}"));
        ysLogsVo.setCreated(BaseEnum.ADMIN.getCode());
        Object[] obj = joinPoint.getArgs();
        ysLogsVo.setParameter(JSON.toJSONString(obj));
        ysLogsVo.setResult(JSON.toJSONString(res));
        ApiLogDetail annotation = signature.getMethod().getAnnotation(ApiLogDetail.class);
        if(Objects.nonNull(annotation)){
            ysLogsVo.setOperation(annotation.operationType().getCode());
            ysLogsVo.setRemark(getDetail(((MethodSignature)joinPoint.getSignature()).getParameterNames(),joinPoint.getArgs(),annotation));
        }
        log.info("发送日志记录信息到数据中心:" + ysLogsVo.toString());
        //发送消息到数据中心
        dataPushApi.sendDataLog(ysLogsVo);
    }

    /**
     * 获取机器ip信息
     * @return
     */
    private InetAddress getIp(){
        InetAddress ip4 = null;
        try {
            ip4 = Inet4Address.getLocalHost();
            return ip4;
        } catch (UnknownHostException e) {
            log.error("获取IP信息异常",e);
        }
        return  null;
    }
    /**
     * 对当前登录用户和占位符处理
     * @param argNames 方法参数名称数组
     * @param args 方法参数数组
     * @param annotation 注解信息
     * @return 返回处理后的描述
     */
    private String getDetail(String[] argNames, Object[] args, ApiLogDetail annotation){

        Map<Object, Object> map = new HashMap<>(4);
        for(int i = 0;i < argNames.length;i++){
            map.put(argNames[i],args[i]);
        }

        String detail = annotation.detail();
        try {
            detail =  annotation.detail();
            for (Map.Entry<Object, Object> entry : map.entrySet()) {
                Object k = entry.getKey();
                Object v = entry.getValue();
                detail = detail.replace("{{" + k + "}}", JSON.toJSONString(v));
            }
        }catch (Exception e){
            e.printStackTrace();
        }
        return detail;
    }

    @Before("operationLog()")
    public void doBeforeAdvice(JoinPoint joinPoint){
        log.info("进入方法前执行.....");

    }

    /**
     * 处理完请求，返回内容
     * @param ret
     */
    @AfterReturning(returning = "ret", pointcut = "operationLog()")
    public void doAfterReturning(Object ret) {
        log.info("方法的返回值 : " + ret);
    }

    /**
     * 后置异常通知
     */
    @AfterThrowing("operationLog()")
    public void throwss(JoinPoint jp){
        log.info("方法异常时执行.....");
    }


    /**
     * 后置最终通知,final增强，不管是抛出异常或者正常退出都会执行
     */
    @After("operationLog()")
    public void after(JoinPoint jp){
        log.info("方法最后执行.....");
    }

}
